package cn.com.cifi.mars.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;

import cn.com.cifi.mars.bean.dto.MymContractDto;
import cn.com.cifi.mars.bean.dto.NosTradeDto;
import cn.com.cifi.mars.bean.dto.VsMarsFeeDto;
import cn.com.cifi.mars.bean.dto.VsMarsGetinDto;
import cn.com.cifi.mars.constant.ActivationEnum;
import cn.com.cifi.mars.constant.Constants;
import cn.com.cifi.mars.constant.FetchNnumberEnum;
import cn.com.cifi.mars.constant.IsDeleteEnum;
import cn.com.cifi.mars.constant.LogEventEnum;
import cn.com.cifi.mars.constant.MyConstants;
import cn.com.cifi.mars.constant.OrgTypeEunm;
import cn.com.cifi.mars.constant.StandardFormatsEnum;
import cn.com.cifi.mars.constant.StandardFormatsMapEnum;
import cn.com.cifi.mars.constant.SysDictionaryTypeEnum;
import cn.com.cifi.mars.entity.MdmProject;
import cn.com.cifi.mars.entity.PanoplanBuildPlanItem;
import cn.com.cifi.mars.entity.PanoplanBuildPlanItemDetail;
import cn.com.cifi.mars.entity.ProjectReturnCoefficient;
import cn.com.cifi.mars.entity.ReturnCoefficientDetail;
import cn.com.cifi.mars.entity.SignPlanVersionSupplyDetail;
import cn.com.cifi.mars.entity.VsNosGetin;
import cn.com.cifi.mars.mapper.mars.ProjectMapper;
import cn.com.cifi.mars.mapper.mars.ProjectReturnCoefficientMapper;
import cn.com.cifi.mars.mapper.mars.ReturnCoefficientDetailMapper;
import cn.com.cifi.mars.mapper.mars.SignPlanVersionSupplyDetailMapper;
import cn.com.cifi.mars.mapper.wrapper.ProjectReturnCoefficientWrapper;
import cn.com.cifi.mars.mapper.wrapper.ReturnCoefficientDetailWrapper;
import cn.com.cifi.mars.service.DmOdsMyVsMarsSContractService;
import cn.com.cifi.mars.service.GenerateReturnCoefficientService;
import cn.com.cifi.mars.service.SignPlanVersionService;
import cn.com.cifi.mars.service.VsMarsFeeService;
import cn.com.cifi.mars.service.VsMarsGetinService;
import cn.com.cifi.mars.service.VsNosPayabledataviewformarsService;
import cn.com.cifi.mars.util.DateUtils;
import cn.com.cifi.mars.util.LocalDateTimeUtils;
import cn.com.cifi.mars.util.SysLogUtils;
import cn.com.cifi.mars.util.UUIDUtils;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class GenerateReturnCoefficientServiceImpl  implements GenerateReturnCoefficientService {
	
	@Autowired
    private ReturnCoefficientDetailWrapper returnCoefficientDetailWrapper;
    @Autowired
    private DmOdsMyVsMarsSContractService dmOdsMyVsMarsSContractService;
    @Autowired
    private SignPlanVersionSupplyDetailMapper signPlanVersionSupplyDetailMapper;
    @Autowired
    private VsMarsFeeService vsMarsFeeService;
    @Autowired
    private VsMarsGetinService vsMarsGetinService;
    @Autowired
    private SignPlanVersionService signPlanVersionService;
    @Autowired
    private ReturnCoefficientDetailMapper returnCoefficientDetailMapper;
    @Autowired
    private ProjectReturnCoefficientServiceImpl returnCoefficientService;
    @Autowired
    private VsNosPayabledataviewformarsService vsNosGetinService;
    @Autowired
    private ProjectMapper projectMapper;
    @Autowired
    MyConstants myConstants;
	
	
	@Override
	public String generateProjectPractical(String projectId) {
		
		MdmProject mdmProject = projectMapper.selectByProjectId(projectId, IsDeleteEnum.NO.getKey());
        String projectName =null;
        if (null != mdmProject){
             projectName = mdmProject.getProjectName();
        }
        //根据项目id获取全盘签约id列表
        List<String> singIdList = signPlanVersionService.getOverallSignVersionList(projectId);
        if (CollectionUtils.isEmpty(singIdList)) {
            return projectName+":项目全盘最新签约计划版本不存在";
        }
        //获取全盘供货详情列表
        List<SignPlanVersionSupplyDetail> supplyDetailList = signPlanVersionSupplyDetailMapper.selectByVersionIdListAndType(singIdList, OrgTypeEunm.ORG_PRODUCT.getKey(), IsDeleteEnum.NO.getKey());
        if (CollectionUtils.isEmpty(singIdList)) {
            SysLogUtils.addSysLog(LogEventEnum.ADD, "生成实际版回款系数失败", "项目id："+projectId+"项目名称："+projectName,"获取全盘供货详情列表失败");
            return projectName+":项目全盘最新签约计划对应的供货详情数据不存在";
        }
		
        //楼栋id(key:标准业态,value:楼栋id 列表)
        Map<String, List<String>> buildingMap = supplyDetailList
                .stream()
                .collect(Collectors.groupingBy(SignPlanVersionSupplyDetail::getProProductCode, Collectors.mapping(SignPlanVersionSupplyDetail::getOrgId, Collectors.toList())));
        //项目下全部楼栋list
        List<String> buildList = getBuildList(buildingMap);
		
        //以标准业态编码分组，获取供货详情 map(key:标准业态编码，value：供货详情列表)
        Map<String, List<SignPlanVersionSupplyDetail>> proProductCodeMap = supplyDetailList.stream()
                .collect(Collectors.groupingBy(SignPlanVersionSupplyDetail::getProProductCode));
		
        //根据项目id查询分期id列表
        List<String> stagesIds = returnCoefficientDetailWrapper.queryStagesIdByProjectId(projectId);
        //根据分期id获取取数来源列表
        Set<String> fetchNums = returnCoefficientDetailWrapper.queryFetchNum(stagesIds);
        if (fetchNums.size() != 1) {
            fetchNums.remove(FetchNnumberEnum.NOS.getKey());
            fetchNums.remove("");
        }
        
        for (String fetchNum : fetchNums) {
            //明源
            if (FetchNnumberEnum.MY.getKey().equals(fetchNum)) {
                //签约记录(签约合同记录表)
                List<MymContractDto> mymContractDtoList = dmOdsMyVsMarsSContractService.selectMymContractListOfActivate(buildList);
                if (CollectionUtils.isEmpty(mymContractDtoList)) {
                    return projectName+":不存在签约记录（合同）";
                }
                //一、计算标准业态下业态第N个月的回款金额
                //应收
                List<VsMarsFeeDto> vsMarsFeeDtoList = vsMarsFeeService.selectByProjectId(projectId);
                //实收
                List<VsMarsGetinDto> vsMarsGetinDtoList = vsMarsGetinService.selectByProjectId(projectId);

                //获取 应收receivableMap（<key:合同id,<key:收款时间,value:金额>>）
                Map<String, Map<LocalDateTime, BigDecimal>> receivableMoneyMap = getReceivableMoneyByRoomIdMap(vsMarsFeeDtoList, mymContractDtoList);
                //获取 实收receiptsMap（<key:合同id,<key:收款时间,value:金额>>）
                Map<String, Map<LocalDateTime, BigDecimal>> receiptsMoneyMap = getReceiptsMoneyByRoomIdMap(vsMarsGetinDtoList, mymContractDtoList);
                //获取容差
                BigDecimal tolerance = myConstants.getTolerance(SysDictionaryTypeEnum.TOLERACE.getKey());
                //1.计算所有合同的回款金额
                //contractGUIDMap(key:合同id，value(key:24月，value：回款金额))
                HashMap<String, Map<Integer, BigDecimal>> contractGUIDMap = getContractGUIDMap(mymContractDtoList, receivableMoneyMap, receiptsMoneyMap, tolerance,projectId,projectName);
                if (MapUtils.isEmpty(contractGUIDMap)){
                    SysLogUtils.addSysLog(LogEventEnum.ADD, "生成实际版回款系数失败", "项目id："+projectId+"项目名称："+projectName,"存在房间没有应收单");
                    return "";
                }

                //获取标准业态下合同id列表（key：标准业态编码，value：合同id列表）
                Map<String, List<String>> contractGUIDListMap = getStandardCodesContractListMap(proProductCodeMap, mymContractDtoList);

                //2获取标准业态下24个月的回款金额(key：标准业态编码，value（key：24月，value：回款金额）)
                Map<String, Map<Integer, BigDecimal>> standardMap = getStandardMap(contractGUIDListMap, mymContractDtoList, contractGUIDMap);

                //标准业态下的签约总金额
                HashMap<String, BigDecimal> grossAmount = getGrossAmount(standardMap);
                //标准业态回款系数Map(key:业态编码,value(key:月份，value：系数))
                HashMap<String, Map<Integer, BigDecimal>> standardFormatsReturn = getStandardFormatsReturn(standardMap, grossAmount);

                //累计签约回款率 =累计回款/总签约金额）map(key:标准业态，value：累计签约回款)
                Map<String, BigDecimal> signingRateMap = getSigningRate(contractGUIDListMap, mymContractDtoList, receiptsMoneyMap);
                //标准业态下最晚交付日期
                Map<String, LocalDateTime> lateFinishDateMap = getLateFinishDate(singIdList);
                //首次签约日期(key:标准业态编码，value：首次签约日期)
                Map<String, LocalDateTime> signFirstTimeMap = getSignFirstTimeMap(mymContractDtoList, contractGUIDListMap);
                //套数去化率 （套数去化率（算）=实际已售套数/实际供货套数）map(key:标准业态，value：套数去化率)
                Map<String, BigDecimal> suiteRateMap = getSuiteRateMap(proProductCodeMap, contractGUIDListMap);
                //生效日期(key：标准业态编码，value：生效日期)
                Map<String, LocalDateTime> effectiveDates = getEffectiveDatesMap(signFirstTimeMap, suiteRateMap);

                //新建版本表
                ProjectReturnCoefficient coefficient = returnCoefficientService.createReality(projectId);
                //新建详情表
                saveRealityDetail(coefficient.getId(), standardFormatsReturn, suiteRateMap, effectiveDates, signingRateMap, signFirstTimeMap);

            } else {
                //签约记录(签约合同记录表)nos
                List<NosTradeDto> nosContractList = returnCoefficientDetailWrapper.selectMymContractListForNos(projectId);

                if (CollectionUtils.isEmpty(nosContractList)) {
                    SysLogUtils.addSysLog(LogEventEnum.ADD, "回款系数", "获取签约记录失败，项目id："+projectId, buildList.toString());
                    return "";
                }
                //删除签约金额小于等于0的合同
                Iterator<NosTradeDto> iterator = nosContractList.iterator();
                while (iterator.hasNext()) {
                    NosTradeDto next = iterator.next();
                    if (next.getAmountMoney().intValue() <= 0) {
                        iterator.remove();
                        SysLogUtils.addSysLog(LogEventEnum.ADD, "回款系数", "合同签约金额小于等于0","项目id："+projectId+"房间id："+next.getRoomId());
                    }
                }

                //实收单
                List<VsNosGetin> nosPayabledataDtoList = vsNosGetinService.selectByProjectId(projectId);
                //获取 实收receiptsMap（<key:房间id,<key:收款时间,value:金额>>）
                Map<String, Map<LocalDateTime, BigDecimal>> receiptsMap = getNosReceiptsMoney(nosPayabledataDtoList, nosContractList);

                List<NosTradeDto> list = new ArrayList<>();
                for (NosTradeDto nosTradeDto : nosContractList) {
                    Set<String> roomIds = receiptsMap.keySet();
                    for (String roomId : roomIds) {
                        if (roomId.equals(nosTradeDto.getRoomId())) {
                            list.add(nosTradeDto);
                        }
                    }
                }

                //1.计算所有房间的回款金额
                //contractGUIDMap(key:房间id，value(key:24月，value：回款金额))
                Map<String, Map<Integer, BigDecimal>> nosContractGUIDMap = getNosContractGUIDMap(list, receiptsMap);

                //2获取标准业态下24个月的回款金额(key：标准业态编码，value（key：24月，value：回款金额）)
                Map<String, Map<Integer, BigDecimal>> standardMap = getStandardMapForNos(list, nosContractGUIDMap);
                //业态编码（key：业态编码，房间id集合）
                Map<String, List<String>> nosCodesOfRoomMap = nosContractList.stream().collect(Collectors.groupingBy(NosTradeDto::getEstateCode, Collectors.mapping(NosTradeDto::getRoomId, Collectors.toList())));

                //标准业态下的实收总金额
                HashMap<String, BigDecimal> grossAmount = getNosGrossAmount(standardMap,nosCodesOfRoomMap);

                //3计算业态第N个月的回款系数:ROUND(业态第N个月的回款金额 / 业态签约总金额, 5)
                //standardFormatsReturn(key:业态编码,value(key:月份，value：系数))
                HashMap<String, Map<Integer, BigDecimal>> standardFormatsReturn = getStandardFormatsReturn(standardMap, grossAmount);


                //首次签约日期(key:标准业态编码，value：首次签约日期)
                Map<String, LocalDateTime> signFirstTimeMap = getSignFirstTimeMapOfNos(list, receiptsMap);


                //标准业态下房间id（key：业态编码，房间id集合）
                Map<String, List<String>> codesContractListMap = list.stream().collect(Collectors.groupingBy(NosTradeDto::getEstateCode, Collectors.mapping(NosTradeDto::getRoomId, Collectors.toList())));
                //标准业态下的签约回款总金额
                //累计签约回款率 =累计回款/总签约金额）map(key:标准业态，value：累计签约回款)(计算全部回款金额)
                Map<String, BigDecimal> signingRateMap = getSigningRateForNos(nosContractList, receiptsMap,codesContractListMap);


                //标准业态下最晚交付日期
                Map<String, LocalDateTime> lateFinishDateMap = getLateFinishDate(singIdList);
                //套数去化率 （套数去化率（算）=实际已售套数/实际供货套数）map(key:标准业态，value：套数去化率)
                codesContractListMap = nosContractList.stream().collect(Collectors.groupingBy(NosTradeDto::getEstateCode, Collectors.mapping(NosTradeDto::getRoomId, Collectors.toList())));
                Map<String, BigDecimal> suiteRateMap = getSuiteRateMap(proProductCodeMap, codesContractListMap);
                //生效日期(key：标准业态编码，value：生效日期)
                Map<String, LocalDateTime> effectiveDates = getEffectiveDatesMap(signFirstTimeMap, suiteRateMap);

                //新建版本表
                ProjectReturnCoefficient coefficient = returnCoefficientService.createRealityForNos(projectId);
                //新建详情表
                saveRealityDetailOfNos(coefficient.getId(), standardFormatsReturn, suiteRateMap, effectiveDates, signFirstTimeMap, signingRateMap);
            }
        }
        
        
        
		return null;
	}
	
	/**
     * 获取项目下全部楼栋list
     * @param buildingMap 楼栋id(key:标准业态,value:楼栋id 列表)
     * @return 全部楼栋list
     */
    private List<String> getBuildList(Map<String, List<String>> buildingMap) {
        List<String> buildList = new ArrayList<>();
        Set<String> builds = buildingMap.keySet();
        for (String build : builds) {
            buildList.addAll(buildingMap.get(build));
        }
        return buildList;
    }

    /**
     * 获取 应收receivableMap（<key:合同id,<key:收款时间,value:金额>>）
     * @param vsMarsFeeDtoList 应收单
     * @return 应收receivableMap（<key:房间id,<key:收款时间,value:金额>>）
     */
    private Map<String, Map<LocalDateTime, BigDecimal>> getReceivableMoneyByRoomIdMap(List<VsMarsFeeDto> vsMarsFeeDtoList, List<MymContractDto> mymContractDtoList) {
        //应收receivableMap（<key:合同id,<key:收款时间,value:金额>>）
        HashMap<String, Map<String, BigDecimal>> receivableMoneyByRoomIdMap = new HashMap<>();
        for (MymContractDto mymContractDto : mymContractDtoList) {
            String contractGUID = mymContractDto.getContractGUID();
            for (VsMarsFeeDto vsMarsFeeDto : vsMarsFeeDtoList) {
                //合同id相同时保存
                if (contractGUID.equals(vsMarsFeeDto.getContractGUID())) {
                    //人民币金额(供款明细金额)
                    BigDecimal receivableAmount = vsMarsFeeDto.getReceiptAmount();
                    //应收时间
                    LocalDateTime receivableDate = vsMarsFeeDto.getReceiptDate();
                    if (receivableDate == null) {
                        continue;
                    }
                    //应收年月
                    String yearMonth = LocalDateTimeUtils.formatTime(receivableDate, DateUtils.DATE_JFP_STR);
                    Map<String, BigDecimal> receiptsMoneyByTimeMap = receivableMoneyByRoomIdMap.get(contractGUID);
                    if (MapUtils.isEmpty(receiptsMoneyByTimeMap)) {
                        Map<String, BigDecimal> receivableMoneyByTimeMap = new HashMap<>();
                        receivableMoneyByTimeMap.put(yearMonth, receivableAmount);
                        receivableMoneyByRoomIdMap.put(contractGUID, receivableMoneyByTimeMap);
                    } else {
                        if (receiptsMoneyByTimeMap.containsKey(yearMonth)) {
                            BigDecimal money = receiptsMoneyByTimeMap.get(yearMonth);
                            receiptsMoneyByTimeMap.put(yearMonth, receivableAmount.add(money));
                        } else {
                            receiptsMoneyByTimeMap.put(yearMonth, receivableAmount);
                        }
                        receivableMoneyByRoomIdMap.put(contractGUID, receiptsMoneyByTimeMap);
                    }
                }
            }
        }

        Map<String, Map<LocalDateTime, BigDecimal>> moneyByRoomIdMap = new HashMap<>();
        for (Entry<String, Map<String, BigDecimal>> contractGUIDEntry : receivableMoneyByRoomIdMap.entrySet()) {
            Map<String, BigDecimal> receivableMoneyByTimeMap = contractGUIDEntry.getValue();
            Map<LocalDateTime, BigDecimal> moneyByTimeMap = new HashMap<>();
            for (Entry<String, BigDecimal> moneyEntry : receivableMoneyByTimeMap.entrySet()) {
                String yearMonth = moneyEntry.getKey();
                LocalDateTime dateTime = LocalDateTimeUtils.toLocalDateTime(yearMonth);
                moneyByTimeMap.put(dateTime, moneyEntry.getValue());
            }
            moneyByRoomIdMap.put(contractGUIDEntry.getKey(), moneyByTimeMap);
        }
        return moneyByRoomIdMap;
    }


    /**
     * 获取 实收receiptsMap（<key:合同id,<key:收款时间,value:金额>>）
     * @param vsMarsGetinDtoList 实收单
     * @return receiptsMap（<key:合同id,<key:收款时间,value:金额>>）
     */
    private Map<String, Map<LocalDateTime, BigDecimal>> getReceiptsMoneyByRoomIdMap(List<VsMarsGetinDto> vsMarsGetinDtoList, List<MymContractDto> mymContractDtoList) {
        //实收receivableMap（<key:合同id,<key:收款时间,value:金额>>）
        HashMap<String, Map<String, BigDecimal>> receiptsMoneyByRoomIdMap = new HashMap<>();
        for (MymContractDto mymContractDto : mymContractDtoList) {
            String contractGUID = mymContractDto.getContractGUID();
            for (VsMarsGetinDto vsMarsGetinDto : vsMarsGetinDtoList) {
                //合同id
                if (contractGUID.equals(vsMarsGetinDto.getContractGUID())) {
                    //人民币金额(供款明细金额)
                    BigDecimal receiptAmount = vsMarsGetinDto.getReceiptAmount();
                    //实收时间
                    LocalDateTime receiptDate = vsMarsGetinDto.getReceiptDate();
                    //实收年月
                    String yearMonth = LocalDateTimeUtils.formatTime(receiptDate, DateUtils.DATE_JFP_STR);
                    if (null == receiptDate || null == receiptAmount) {
                        continue;
                    }
                    //当前月
                    String nowYearMonth = LocalDateTimeUtils.formatTime(LocalDateTime.now(), DateUtils.DATE_JFP_STR);
                    /**
                     * 实收在当前月不计算数据
                     */
                    if (nowYearMonth.equals(yearMonth)) {
                        continue;
                    }
                    Map<String, BigDecimal> receiptsMoneyByTimeMap = receiptsMoneyByRoomIdMap.get(contractGUID);

                    if (MapUtils.isEmpty(receiptsMoneyByTimeMap)) {
                        Map<String, BigDecimal> receivableMoneyByTimeMap = new HashMap<>();
                        receivableMoneyByTimeMap.put(yearMonth, receiptAmount);
                        receiptsMoneyByRoomIdMap.put(contractGUID, receivableMoneyByTimeMap);
                    } else {
                        if (receiptsMoneyByTimeMap.containsKey(yearMonth)) {
                            BigDecimal money = receiptsMoneyByTimeMap.get(yearMonth);

                            receiptsMoneyByTimeMap.put(yearMonth, receiptAmount.add(money));
                        } else {
                            receiptsMoneyByTimeMap.put(yearMonth, receiptAmount);
                        }
                        receiptsMoneyByRoomIdMap.put(contractGUID, receiptsMoneyByTimeMap);
                    }
                }
            }
        }


        Map<String, Map<LocalDateTime, BigDecimal>> moneyByRoomIdMap = new HashMap<>();
        for (Entry<String, Map<String, BigDecimal>> contractGUIDEntry : receiptsMoneyByRoomIdMap.entrySet()) {
            Map<String, BigDecimal> receivableMoneyByTimeMap = contractGUIDEntry.getValue();
            Map<LocalDateTime, BigDecimal> moneyByTimeMap = new HashMap<>();
            for (Entry<String, BigDecimal> moneyEntry : receivableMoneyByTimeMap.entrySet()) {
                String yearMonth = moneyEntry.getKey();
                LocalDateTime dateTime = LocalDateTimeUtils.toLocalDateTime(yearMonth);
                moneyByTimeMap.put(dateTime, moneyEntry.getValue());
            }
            moneyByRoomIdMap.put(contractGUIDEntry.getKey(), moneyByTimeMap);
        }
        return moneyByRoomIdMap;
    }
    
    /**
     * 计算所有合同的回款金额
     * @param mymContractDtoList 签约记录(签约合同记录表)
     * @param receivableMoneyMap 应收
     * @param receiptsMoneyMap   实收
     */
    private HashMap<String, Map<Integer, BigDecimal>> getContractGUIDMap(List<MymContractDto> mymContractDtoList, Map<String, Map<LocalDateTime, BigDecimal>> receivableMoneyMap, Map<String, Map<LocalDateTime, BigDecimal>> receiptsMoneyMap, BigDecimal tolerance,String projectId,String projectName) {
        HashMap<String, Map<Integer, BigDecimal>> contractGUIDMap = new HashMap<>();
        //遍历签约合同记录表获取全部合同的每月回款金额
        for (MymContractDto mymContractDto : mymContractDtoList) {
            //合同id
            String contractGUID = mymContractDto.getContractGUID();
            //签约激活状态
            String status = mymContractDto.getStatus();
            if (ActivationEnum.CLOSE.getKey().equals(status)) {
                continue;
            }
            //签约时间
            LocalDateTime ywgsDate = mymContractDto.getYwgsDate();
            //签约成交总价
            BigDecimal cjRmbTotal = mymContractDto.getCjRmbTotal();
            //补差金额
            BigDecimal bcTotal = mymContractDto.getBcTotal();
            //补差审核日期
            LocalDateTime auditDate = mymContractDto.getAuditDate();

            //房间签约回款场景及场景对应的房源第N个月回款金额计算map(key；24月，value：每月回款金额)
            Map<Integer, BigDecimal> amountTimeMap = new HashMap<>();

            //BigDecimal total 应收总金额
            BigDecimal receiptTotal = BigDecimal.ZERO;

            //获取应收map集合
            Map<LocalDateTime, BigDecimal> receivablesMap = receivableMoneyMap.get(contractGUID);
            if (MapUtils.isEmpty(receivablesMap)) {
                return null;
            }
            //应收receivablesMoneyOfTimeMap(key:时间，value：金额)
            HashMap<LocalDateTime, BigDecimal> receivablesMoneyOfTimeMap = new HashMap<>();
            Set<LocalDateTime> receivablesTimes = receivablesMap.keySet();
            for (LocalDateTime receivablesTime : receivablesTimes) {
                BigDecimal money = receivablesMap.get(receivablesTime);
                receiptTotal = receiptTotal.add(money);
                receivablesMoneyOfTimeMap.put(receivablesTime, money);

            }

            //脏数据（应收总额与合同金额差绝对值大于100）
            if (receiptTotal.subtract(cjRmbTotal).abs().intValue() > tolerance.intValue()) {
                continue;
            }
            //BigDecimal total 实收总额
            BigDecimal total = BigDecimal.ZERO;
            //实收receivablesMoneyOfTimeMap(key:时间，value：金额)
            HashMap<LocalDateTime, BigDecimal> receiptsMoneyOfTimeMap = new HashMap<>();

            //获取实收map集合
            Map<LocalDateTime, BigDecimal> receiptsMap = receiptsMoneyMap.get(contractGUID);
            if(CollectionUtils.isNotEmpty(receiptsMap)){
                for (LocalDateTime receivablesTime : receiptsMap.keySet()) {
                    BigDecimal money = receiptsMap.get(receivablesTime);
                    total = total.add(money);
                    receiptsMoneyOfTimeMap.put(receivablesTime, money);
                }
            }

            //1、房源已收回全款:以实收款项计算房源第N个月的回款金额; total 实收总金额,cjRmbTotal：签约成交总价
            //容差 差距不超过100算已收回全款
            if (total.subtract(cjRmbTotal).abs().intValue() <= tolerance.intValue()) {
                int i = 0;
                //首付款
                BigDecimal downPayment = BigDecimal.ZERO;
                //实收部分以实收单计算
                Set<LocalDateTime> localDateTimes = receiptsMoneyOfTimeMap.keySet();
                for (LocalDateTime localDateTime : localDateTimes) {
                    i = LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime);
                    if (i <= 0) {
                        //计算首付款
                        downPayment = downPayment.add(receiptsMoneyOfTimeMap.get(localDateTime));
                    }
                }
                //第一个月回款系数
                i = 1;
                amountTimeMap.put(i, downPayment);
                for (LocalDateTime localDateTime : localDateTimes) {
                    i = LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime);
                    if (i > 0) {
                        amountTimeMap.put(++i, receiptsMoneyOfTimeMap.get(localDateTime));
                    }
                }
            }
            //2、房源已收回全款，包含补差:以实收款项计算房源第N个月的回款金额，补差金额添加到最后一笔实收款所属月份;
            //补差审核时期存在时补差生效
            if (auditDate != null) {
                if ((total.add(bcTotal).intValue() - cjRmbTotal.intValue()) <= tolerance.intValue()) {
                    //实收部分以实收单计算
                    for (LocalDateTime receiptsTime : receiptsMoneyOfTimeMap.keySet()) {
                        int diff = LocalDateTimeUtils.monthDiff(ywgsDate, receiptsTime);
                        if (diff <= 0) {
                            //计算首付款
                            if(CollectionUtils.isEmpty(amountTimeMap)){
                                amountTimeMap.put(1,receiptsMoneyOfTimeMap.get(receiptsTime));
                            }else {
                                amountTimeMap.put(1, amountTimeMap.get(1).add(receiptsMoneyOfTimeMap.get(receiptsTime)));
                            }
                        }else {
                            amountTimeMap.put(++diff, receiptsMoneyOfTimeMap.get(receiptsTime));
                        }
                    }

                    //补差款
                    LocalDateTime maxTime = receiptsMoneyOfTimeMap.entrySet().stream().max(Comparator.comparing(Entry::getKey)).get().getKey();
                    int diff = LocalDateTimeUtils.monthDiff(ywgsDate, maxTime);
                    amountTimeMap.put(diff+2, bcTotal);
                }
            }

            /**
             * 房源部分实收部分应收：以实收款项计算房源第N个月的回款金额，判断实收部分是否符合供款明细，
             * 符合：则剩余部分为供款明细应收。
             * 不符合：应收部分以供款明细计算房源第N个月的回款金额，
             * 少收或多收部分添加到供款明细对应实收日期的下个月的供款明细，并依次顺延;
             * 如果实收日期大于供款明细最后日期则按照（当前月）收回全部剩余款项为应收款项
             */
            //3.房源未收回全款，
            if (total.intValue() >= 0 && total.intValue() < cjRmbTotal.intValue()) {
                int i = 0;
                //首付款
                BigDecimal downPayment = BigDecimal.ZERO;
                //实收部分以实收单计算
                Set<LocalDateTime> localDateTimes = receiptsMoneyOfTimeMap.keySet();
                for (LocalDateTime localDateTime : localDateTimes) {
                    i = LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime);
                    if (i <= 0) {
                        //计算首付款
                        downPayment = downPayment.add(receiptsMoneyOfTimeMap.get(localDateTime));
                    }
                }
                i = 1;
                amountTimeMap.put(i, downPayment);

                for (LocalDateTime localDateTime : localDateTimes) {
                    i = LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime);
                    if (i > 0) {
                        amountTimeMap.put(++i, receiptsMoneyOfTimeMap.get(localDateTime));
                    }
                }
                if (i < 1) {
                    i = 1;
                }
                //获取实收最后时间
                LocalDateTime receiptsLastTime = receiptsMoneyOfTimeMap.entrySet().stream().max(Comparator.comparing(Entry::getKey)).get().getKey();
                //获取应收最后时间
                LocalDateTime receivableLastTime = receivablesMoneyOfTimeMap.entrySet().stream().max(Comparator.comparing(Entry::getKey)).get().getKey();
                //根据实收最后时间 获取 实收最后时间的 应收总额
                BigDecimal receiptTotalMoney = getReceiptTotalMoney(receiptsLastTime, receivablesMoneyOfTimeMap);
                //获取截止到当前时间的上个月的应收总额
                BigDecimal money = getReceiptTotalMoney(LocalDateTimeUtils.getLastDayOfLastMonth(), receivablesMoneyOfTimeMap);

                //判断实收金额是否与应收金额相等(实收总额减去应收总额小于100)
                if (total.subtract(receiptTotalMoney).abs().intValue() < tolerance.intValue()) {
                    //应收总额减去实收总额为未收到金额
                    money = money.subtract(total);
                    //判断应收单是否在当前时间之后  如果在当前时间之后按应收单计算  之前则累加到当前月放到当前月
                    //相等则剩余部分按照应收金额计算回款金额
                    Set<LocalDateTime> receivableLocalDateTimes = receivablesMoneyOfTimeMap.keySet();
                    for (LocalDateTime localDateTime : receivableLocalDateTimes) {
                        //当前月份有应收单
                        if (LocalDateTimeUtils.toIntYearMonth(localDateTime) == LocalDateTimeUtils.toIntYearMonth(LocalDateTime.now())) {
                            //当前时间的应收总额中的未收部分增加到当前月的应收单
                            amountTimeMap.put(LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime) + 1, receivablesMoneyOfTimeMap.get(localDateTime).add(money));

                        }  //应收单时间在当前时间之后
                        else if (LocalDateTime.now().compareTo(localDateTime) < 0) {
                            int j = LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime);
                            if (j > i) {
                                amountTimeMap.put(++j, receivablesMoneyOfTimeMap.get(localDateTime));
                            }
                        }
                    }
                    Set<Integer> integers = amountTimeMap.keySet();
                    if (!integers.contains(LocalDateTimeUtils.monthDiff(ywgsDate, LocalDateTime.now()) + 1)) {
                        //当前月份没有应收单 就把当前月份上个月的应收总额添加到当前月
                        amountTimeMap.put(LocalDateTimeUtils.monthDiff(ywgsDate, LocalDateTime.now()) + 1, money);
                    }
                }
                //不相等则先判断当前日期的上个月最后一天是否在最大拱款明细日期之后
                else if (LocalDateTimeUtils.getLastDayOfLastMonth().isAfter(receivableLastTime)) {
                    //如果超过则把全款剩余款项计算为当前月，超过24月则算为第24月
                    BigDecimal residueMoney = cjRmbTotal.subtract(total);
                    int k = LocalDateTimeUtils.monthDiff(ywgsDate, LocalDateTime.now());
                    if (k > 24) {
                        k = 24;
                    }
                    amountTimeMap.put(k, residueMoney);
                } else {
                    //不超过则把少收或多收部分添加到供款明细的当前月
                    //应收总额减去实收总额为未收到金额
                    money = money.subtract(total);
                    //遍历应收单
                    Set<LocalDateTime> receivablesOfTimes = receivablesMoneyOfTimeMap.keySet();
                    for (LocalDateTime localDateTime : receivablesOfTimes) {
                        //当前月份有应收单
                        if (LocalDateTimeUtils.toIntYearMonth(localDateTime) == LocalDateTimeUtils.toIntYearMonth(LocalDateTime.now())) {
                            amountTimeMap.put(LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime) + 1, receivablesMoneyOfTimeMap.get(localDateTime).add(money));

                        }  //应收单时间在当前时间之后
                        else if (LocalDateTime.now().compareTo(localDateTime) < 0) {
                            int j = LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime);
                            if (j > i) {
                                amountTimeMap.put(++j, receivablesMoneyOfTimeMap.get(localDateTime));
                            }
                        }
                    }
                    Set<Integer> integers = amountTimeMap.keySet();
                    if (!integers.contains(LocalDateTimeUtils.monthDiff(ywgsDate, LocalDateTime.now()) + 1)) {
                        //当前月份没有应收单 就把当前月份上个月的应收总额添加到当前月
                        amountTimeMap.put(LocalDateTimeUtils.monthDiff(ywgsDate, LocalDateTime.now()) + 1, money);
                    }
                }
            }

            //4.、房源已收回全款，但回款金额超过签约金额:以实收款项计算房源第N个月的回款金额，
            // 超收金额算到（当前年月）的回款退款（负的超收金额）
            if (total.intValue() - cjRmbTotal.intValue() > tolerance.intValue()) {
                int i = 0;
                //首付款
                BigDecimal downPayment = BigDecimal.ZERO;
                //实收部分以实收单计算
                Set<LocalDateTime> localDateTimes = receiptsMoneyOfTimeMap.keySet();
                for (LocalDateTime localDateTime : localDateTimes) {
                    i = LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime);
                    if (i <= 0) {
                        //计算首付款
                        downPayment = downPayment.add(receiptsMoneyOfTimeMap.get(localDateTime));
                    }
                }
                //第一个月之后回款系数
                i = 1;
                amountTimeMap.put(i, downPayment);
                for (LocalDateTime localDateTime : localDateTimes) {
                    i = LocalDateTimeUtils.monthDiff(ywgsDate, localDateTime);
                    if (i > 0) {
                        amountTimeMap.put(++i, receiptsMoneyOfTimeMap.get(localDateTime));
                    }
                }
                if (i < 1) {
                    i = 1;
                }
                //超收金额
                BigDecimal overcharge = total.subtract(cjRmbTotal);
                //正负一百不算
                if (overcharge.abs().intValue() <= tolerance.intValue()) {
                    overcharge = BigDecimal.ZERO;
                }
                //当前月回款系数为负数
                amountTimeMap.put(LocalDateTimeUtils.monthDiff(ywgsDate, LocalDateTime.now()), overcharge);
            }
            contractGUIDMap.put(contractGUID, amountTimeMap);
        }
        return contractGUIDMap;
    }
    
    /**
     * 获取标准业态下合同id列表
     */
    private Map<String, List<String>> getStandardCodesContractListMap(Map<String, List<SignPlanVersionSupplyDetail>> proProductCodeMap, List<MymContractDto> mymContractDtoList) {
        //标准业态下合同列表(key:业态编码,value:合同列表)
        Map<String, List<String>> standardCodesContractListMap = new HashMap<>();
        //遍历所有业态下楼栋id列表
        //bldguidMap(key:楼栋id，value:合同id列表)

        Map<String, List<String>> contractMap = mymContractDtoList.stream()
                .filter(m -> m.getStatus().equals(ActivationEnum.ACTIVATION.getKey()))
                .collect(Collectors.groupingBy(MymContractDto::getBldPrdID,
                        Collectors.mapping(MymContractDto::getContractGUID, Collectors.toList())));

        Set<String> bldguidList = contractMap.keySet();

        //标准业态下全部楼栋id列表standardBuild(key:标准业态编码,value:楼栋id列表)
        //遍历standardBuild
        Map<String, List<String>> standardBuild = getStandardBuild(proProductCodeMap);
        //获取所有标准业态下楼栋集合

        for (Entry<String, List<String>> entry : standardBuild.entrySet()) {
            List<String> bldguids = entry.getValue();
            ArrayList<String> list = new ArrayList<>();
            for (String bldguid : bldguids) {

                List<String> contractIds = contractMap.get(bldguid);

                if (CollectionUtils.isNotEmpty(contractIds)) {
                    list.addAll(contractIds);
                }
            }
            standardCodesContractListMap.put(entry.getKey(), list);

        }

        Iterator<String> iter = standardCodesContractListMap.keySet().iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            List<String> list = standardCodesContractListMap.get(key);
            if (CollectionUtils.isEmpty(list)) {
                iter.remove();
            }
        }

        return standardCodesContractListMap;
    }
    
    /**
     * 获取标准业态下24个月的回款金额(key：标准业态编码，value（key：24月，value：回款金额）)
     *  @param proProductCodeMap  供货详情 map(key:标准业态编码，value：供货详情列表)
     * @param contractGUIDListMap
     * @param mymContractDtoList 签约记录(签约合同记录表)
     */
    private Map<String, Map<Integer, BigDecimal>> getStandardMap(Map<String, List<String>> contractGUIDListMap, List<MymContractDto> mymContractDtoList, HashMap<String, Map<Integer, BigDecimal>> contractGUIDMap) {

        //结果数据Map(key:业态编码,value:(key:月数,value:金额))
        Map<String, Map<Integer, BigDecimal>> standardMap = new HashMap<>();
        for (Map.Entry<String, List<String>> entry : contractGUIDListMap.entrySet()) {
            Map<Integer, BigDecimal> moneyByMonthMap = getMoneyByMonthMap();

            List<String>  contractGUIDList = entry.getValue();

            contractGUIDMap.entrySet().stream()
                    .filter(s -> contractGUIDList.contains(s.getKey())).map(Map.Entry::getValue)
                    .forEach(s ->
                            s.entrySet().stream()
                                    .forEach(e -> {
                                        if (e.getKey() > 24) {
                                            moneyByMonthMap.put(24, moneyByMonthMap.get(24).add(e.getValue()));
                                        } else {
                                            moneyByMonthMap.put(e.getKey(), moneyByMonthMap.get(e.getKey()).add(e.getValue()));
                                        }
                                    }));
            standardMap.put(entry.getKey(), moneyByMonthMap);
        }

        return standardMap;
    }
    
    /**
     * 获取标准业态下的签约总金额
     */
    private HashMap<String, BigDecimal> getGrossAmount(Map<String, Map<Integer, BigDecimal>> standardMap) {

        //标准业态下的签约总金额(key:标准业态编码，value：签约总金额)
        HashMap<String, BigDecimal> codeGrossAmount = new HashMap<>();
        Set<String> codes = standardMap.keySet();

        for (String code : codes) {
            BigDecimal totalMoney = BigDecimal.ZERO;
            Map<Integer, BigDecimal> map = standardMap.get(code);
            Set<Integer> integers = map.keySet();
            for (Integer integer : integers) {
                totalMoney = totalMoney.add(map.get(integer));
            }
            codeGrossAmount.put(code, totalMoney);

        }
        return codeGrossAmount;
    }

    /**
     * 计算业态第N个月的回款系数:ROUND(业态第N个月的回款金额 / 业态签约总金额, 5) * 100%;
     *
     * @param standardMap 标准业态下24个月的回款金额
     * @param grossAmount 标准业态下的签约总金额
     * @return standardFormatsReturn(key : 业态编码, value ( key : 月份 ， value ： 系数))
     */
    private HashMap<String, Map<Integer, BigDecimal>> getStandardFormatsReturn(Map<String, Map<Integer, BigDecimal>> standardMap, HashMap<String, BigDecimal> grossAmount) {
        //standardFormatsReturn(key : 业态编码, value ( key : 月份 ， value ： 系数))
        HashMap<String, Map<Integer, BigDecimal>> standardFormatsReturn = new HashMap<>();

        //获取标准业态下24个月的回款金额standardMap
        Set<String> standardCode = standardMap.keySet();
        for (String s : standardCode) {
            //coefficientMap(key:月份,value:系数)
            HashMap<Integer, BigDecimal> coefficientMap = new HashMap<>();
            Map<Integer, BigDecimal> map = standardMap.get(s);
            Set<Integer> integers = map.keySet();
            //grossAmount标准业态下的签约总金额
            BigDecimal sumMoney = grossAmount.get(s);

            if (sumMoney.equals(BigDecimal.ZERO)) {
                continue;
            }
            for (Integer integer : integers) {
                coefficientMap.put(integer, map.get(integer).divide(sumMoney, 5, RoundingMode.HALF_UP));
            }
            standardFormatsReturn.put(s, coefficientMap);
        }

        Set<Entry<String, Map<Integer, BigDecimal>>> standardCodes = standardFormatsReturn.entrySet();
        for (Entry<String, Map<Integer, BigDecimal>> codes : standardCodes) {
            Map<Integer, BigDecimal> coefficientMap = codes.getValue();
            Set<Entry<Integer, BigDecimal>> entrySet = coefficientMap.entrySet();
            //系数和
            BigDecimal total=BigDecimal.ZERO;
            for (Entry<Integer, BigDecimal> e : entrySet) {
               total= total.add(e.getValue());
            }
            //判断系数和是否小于容差 小于则把差距放到最后一个月
            if (BigDecimal.ONE.subtract(total).abs().doubleValue()<=0.00024){
                coefficientMap.put(24,coefficientMap.get(24).add(BigDecimal.ONE.subtract(total)));
                standardFormatsReturn.put(codes.getKey(), coefficientMap);
            }else {
                SysLogUtils.addSysLog(LogEventEnum.ADD, "生成实际版回款系数警告", "警告！！！","实际版回款系数总和大于容差0.00024");
            }
        }
        return standardFormatsReturn;
    }
    
    /**
     * 计算标准业态下累计签约回款率
     */
    private Map<String, BigDecimal> getSigningRate(Map<String, List<String>> contractGUIDListMap, List<MymContractDto> mymContractDtoList, Map<String, Map<LocalDateTime, BigDecimal>> receiptsMoneyMap) {

        Map<String, BigDecimal> signingRatMap = new HashMap<>();
        //签约总金额
        Map<String, BigDecimal> grossAmount = getMyGrossAmount(mymContractDtoList,contractGUIDListMap);
        //获取标准业态下全部合同
        Set<String> codes = contractGUIDListMap.keySet();
        //标准业态下的实收金额（key：标准业态编码，value：实收金额）
        Map<String, BigDecimal> contractPracticalMoney = new HashMap<>();
        for (String code : codes) {
            // 标准业态下全部实收金额
            BigDecimal practicalMoney = BigDecimal.ZERO;
            //标准业态code下合同id列表
            List<String> contractIds = contractGUIDListMap.get(code);
            for (String contractId : contractIds) {
                //全部合同mymContractDtoList
                for (MymContractDto mymContractDto : mymContractDtoList) {
                    String contractGUID = mymContractDto.getContractGUID();
                    String status = mymContractDto.getStatus();
                    if (ActivationEnum.CLOSE.getKey().equals(status)) {
                        continue;
                    }
                    //合同id等于标准业态下全部合同id
                    if (contractGUID.equals(contractId)) {
                        //实收单
                        Map<LocalDateTime, BigDecimal> localDateTimeBigDecimalMap = receiptsMoneyMap.get(contractGUID);
                        if (MapUtils.isEmpty(localDateTimeBigDecimalMap)) {
                            continue;
                        }
                        Set<LocalDateTime> localDateTimes = localDateTimeBigDecimalMap.keySet();
                        for (LocalDateTime localDateTime : localDateTimes) {
                            BigDecimal bigDecimal = localDateTimeBigDecimalMap.get(localDateTime);
                            practicalMoney = practicalMoney.add(bigDecimal);
                        }
                    }
                }

            }
            contractPracticalMoney.put(code, practicalMoney);
        }
        Set<String> standardCodes = contractPracticalMoney.keySet();
        for (String standardCode : standardCodes) {
            //标准业态下签约总金额
            BigDecimal grossAmountMoney = grossAmount.get(standardCode);
            if (grossAmountMoney.equals(BigDecimal.ZERO)) {
                continue;
            }
            //标准业态下实收总金额
            BigDecimal practicalMoney = contractPracticalMoney.get(standardCode);
            if (practicalMoney.equals(BigDecimal.ZERO)) {
                continue;
            }
            BigDecimal rate = practicalMoney.divide(grossAmountMoney, 5, RoundingMode.HALF_UP);
            signingRatMap.put(standardCode, rate);
        }
        return signingRatMap;
    }
    
    /**
     * 获取最后签约日期
     *
     * @param singIdList
     * @return
     */
    private Map<String, LocalDateTime> getLateFinishDate(List<String> singIdList) {


        Map<String, LocalDateTime> lateFinishDateMap = new HashMap<>();
        //map(key:标准业态：value：id集合List<String>)
        Map<String, List<String>> pkIdMap = new HashMap<>();

        // 2.1首先通过签约版本id列表 查询 t_sign_plan_version 签约版本表 中的货值版本id 列表

        // 2.2通过货值版本id列表 查询t_price_version 货值版本表中的 楼栋计划版本id 列表
        // 2.3通过楼栋计划版本id列表 查询t_panoplan_build_plan_item 楼栋版本计划楼栋表 中的id 列表

        Map<String, List<String>> standardCodeMap = getStandardCodeMap(singIdList);
        Set<String> codes = standardCodeMap.keySet();
        List<PanoplanBuildPlanItem> buildPlanList = returnCoefficientDetailWrapper.queryBySingIdList(singIdList);

        for (String code : codes) {

            ArrayList<String> list = new ArrayList<>();
            for (PanoplanBuildPlanItem panoplanBuildPlanItem : buildPlanList) {

                String productTypeNum = panoplanBuildPlanItem.getProductTypeNum();
                String id = panoplanBuildPlanItem.getId();


                List<String> childCodes = standardCodeMap.get(code);
                for (String childCode : childCodes) {
                    if (productTypeNum.equals(childCode)) {
                        list.add(id);
                    }
                }
            }
            pkIdMap.put(code, list);
        }

        // 2.4 根据 id集合 对应 t_panoplan_build_plan_item_detail  楼栋版本计划楼栋详情表
        Set<String> codeSet = pkIdMap.keySet();
        for (String code : codeSet) {
            LocalDateTime lateFinishDate = LocalDateTime.MIN;
            List<String> pkIdsList = pkIdMap.get(code);

            List<PanoplanBuildPlanItemDetail> buildPlanItemDetails = returnCoefficientDetailWrapper.queryByPkIdList(pkIdsList);
            for (PanoplanBuildPlanItemDetail buildPlanItemDetail : buildPlanItemDetails) {
                //计划完成时间
                LocalDateTime plannedFinishDate = buildPlanItemDetail.getPlannedFinishDate();
                //实际完成时间
                LocalDateTime actualFinishDate = buildPlanItemDetail.getActualFinishDate();
                if (null == actualFinishDate) {
                    if (plannedFinishDate == null) {
                        continue;
                    }
                    if (lateFinishDate.isBefore(plannedFinishDate)) {
                        lateFinishDate = plannedFinishDate;
                    }
                } else {
                    if (lateFinishDate.isBefore(actualFinishDate)) {
                        lateFinishDate = actualFinishDate;
                    }
                }
            }
            lateFinishDateMap.put(code, lateFinishDate);
        }

        return lateFinishDateMap;
    }
    
    /**
     * 获取首次签约日期
     *
     * @param mymContractDtoList   合同
     * @param codesContractListMap 标准业态下合同id集合
     * @return
     */
    private Map<String, LocalDateTime> getSignFirstTimeMap(List<MymContractDto> mymContractDtoList, Map<String, List<String>> codesContractListMap) {
        Map<String, LocalDateTime> signFirstTimeMap = new HashMap<>();
        //首次签约日期

        Set<String> codes = codesContractListMap.keySet();
        for (String code : codes) {
            LocalDateTime signFirstTime = LocalDateTime.now();
            List<String> contracts = codesContractListMap.get(code);
            for (String contractId : contracts) {
                for (MymContractDto mymContractDto : mymContractDtoList) {
                    //签约时间
                    String status = mymContractDto.getStatus();
                    String contractGUID = mymContractDto.getContractGUID();

                    if (ActivationEnum.CLOSE.getKey().equals(status)) {
                        continue;
                    }

                    if (contractGUID.equals(contractId)) {
                        LocalDateTime ywgsDate = mymContractDto.getYwgsDate();
                        //如果签约时间大于signFirstTime则首次签约时间为本次签约时间
                        if (ywgsDate.isBefore(signFirstTime)) {
                            signFirstTime = ywgsDate;
                        }
                    }

                }

            }

            signFirstTimeMap.put(code, signFirstTime);
        }
        return signFirstTimeMap;
    }
    
    /**
     * 获取套数去化率（套数去化率=实际已售套数/实际供货套数）map(key:标准业态，value：套数去化率)
     *
     * @return
     */
    private Map<String, BigDecimal> getSuiteRateMap(Map<String, List<SignPlanVersionSupplyDetail>> proProductCodeMap, Map<String, List<String>> codesContractListMap) {
        //套数去化率（map(key:标准业态，value：套数去化率)
        Map<String, BigDecimal> suiteRateMap = new HashMap<>();

        //获取标准业态下供货套数
        //map(key：标准业态，value：供货套数)
        HashMap<String, Integer> mapForProvide = new HashMap<>();
        Set<String> productCodes = proProductCodeMap.keySet();
        for (String productCode : productCodes) {
            int sum = 0;
            List<SignPlanVersionSupplyDetail> signPlanVersionSupplyDetails = proProductCodeMap.get(productCode);
            for (SignPlanVersionSupplyDetail signPlanVersionSupplyDetail : signPlanVersionSupplyDetails) {
                Integer roomNum = signPlanVersionSupplyDetail.getRoomNumActual();
                Integer parkingNum = signPlanVersionSupplyDetail.getParkingNumActual();

                sum = sum + roomNum + parkingNum;
            }

            mapForProvide.put(productCode, sum);
        }

        //标准业态下合同列表(key:业态编码,value:合同列表)
        //标准业态下已售套数
        HashMap<String, Integer> mapForPractical = new HashMap<>();
        Set<String> codes = codesContractListMap.keySet();
        for (String code : codes) {
            List<String> list = codesContractListMap.get(code);

            mapForPractical.put(code, list.size());
        }

        Set<String> standardCodes = mapForPractical.keySet();
        for (String standardCode : standardCodes) {
            //实售
            BigDecimal practical = new BigDecimal(mapForPractical.get(standardCode));
            //供货
            BigDecimal provide = new BigDecimal(mapForProvide.get(standardCode));
            if (practical.intValue() == 0) {
                suiteRateMap.put(standardCode, BigDecimal.ZERO);
                continue;
            }
            if (provide.intValue() == 0) {
                suiteRateMap.put(standardCode, BigDecimal.ZERO);
                continue;
            }
            suiteRateMap.put(standardCode, practical.divide(provide, 5, RoundingMode.HALF_UP));
        }
        return suiteRateMap;
    }
    
    /**
     * 1.计算所有房间的回款金额 nos
     * nosContractGUIDMap(key:房间id，value(key:24月，value：回款金额))
     *
     * @param nosContractList 合同列表
     * @param receiptsMap     实收单
     * @return
     */
    private Map<String, Map<Integer, BigDecimal>> getNosContractGUIDMap(List<NosTradeDto> nosContractList, Map<String, Map<LocalDateTime, BigDecimal>> receiptsMap) {
        //(key:房间id，value(key:24月，value：回款金额))
        Map<String, Map<Integer, BigDecimal>> nosContractGUIDMap = new HashMap<>();

        for (NosTradeDto nosTradedataDto : nosContractList) {
            //房间id
            String roomId = nosTradedataDto.getRoomId();
            //房间成交金额
            BigDecimal tradeMoney = nosTradedataDto.getAmountMoney();
            if (tradeMoney.intValue() == 0) {
                continue;
            }
            //成交日期
            LocalDateTime tradeDate = nosTradedataDto.getTradeDate();


            //实收单
            Map<LocalDateTime, BigDecimal> timeOfMoneyMap = receiptsMap.get(roomId);
            //实收总额
            BigDecimal totalMoney = BigDecimal.ZERO;
            Set<LocalDateTime> localDateTimes = timeOfMoneyMap.keySet();
            for (LocalDateTime localDateTime : localDateTimes) {
                //实收金额
                BigDecimal money = timeOfMoneyMap.get(localDateTime);


                totalMoney = totalMoney.add(money);
            }

            //房间签约回款场景及场景对应的房源第N个月回款金额计算map(key；1-24月，value：每月回款金额)
            Map<Integer, BigDecimal> amountTimeMap = new HashMap<>();

            //实收总额大于等于合同金额
            if (totalMoney.intValue() > tradeMoney.intValue()) {
                int i = 0;
                BigDecimal differenceMoney = tradeMoney.subtract(totalMoney);
                if (differenceMoney.abs().intValue() < 100) {
                    differenceMoney = BigDecimal.ZERO;
                }
                //实收时间集合
                Set<LocalDateTime> receiptsTimes = timeOfMoneyMap.keySet();
                BigDecimal firstMoney = BigDecimal.ZERO;
                //签约日期之前的实际回款和签约日期当月的回款全部计算到第一月的实际回款
                for (LocalDateTime receiptsTime : receiptsTimes) {
                    i = LocalDateTimeUtils.monthDiff(tradeDate, receiptsTime);
                    if (i <= 0) {
                        firstMoney = firstMoney.add(timeOfMoneyMap.get(receiptsTime));
                    }

                }
                i = 1;
                amountTimeMap.put(i, firstMoney);
                //签约日期之后的实际回款计算
                for (LocalDateTime receiptsTime : receiptsTimes) {
                    i = LocalDateTimeUtils.monthDiff(tradeDate, receiptsTime);
                    if (i > 0) {
                        amountTimeMap.put(++i, timeOfMoneyMap.get(receiptsTime));
                    }
                }
                //超收金额放到当前月计算退款为负值
                if (i < 1) {
                    i = 1;
                }
                amountTimeMap.put(LocalDateTimeUtils.monthDiff(tradeDate, LocalDateTime.now())+1, differenceMoney);

            } else if (totalMoney.intValue() == tradeMoney.intValue()) {
                int i = 0;
                //实收时间集合
                Set<LocalDateTime> receiptsTimes = timeOfMoneyMap.keySet();
                BigDecimal firstMoney = BigDecimal.ZERO;
                //签约日期之前的实际回款和签约日期当月的回款全部计算到第一月的实际回款
                for (LocalDateTime receiptsTime : receiptsTimes) {
                    i = LocalDateTimeUtils.monthDiff(tradeDate, receiptsTime);
                    if (i <= 0) {
                        firstMoney = firstMoney.add(timeOfMoneyMap.get(receiptsTime));
                    }

                }
                i = 1;
                amountTimeMap.put(i, firstMoney);
                //签约日期之后的实际回款计算

                for (LocalDateTime receiptsTime : receiptsTimes) {
                    i = LocalDateTimeUtils.monthDiff(tradeDate, receiptsTime);
                    if (i > 0) {
                        amountTimeMap.put(++i, timeOfMoneyMap.get(receiptsTime));
                    }
                }

            } else {
                continue;
            }

            nosContractGUIDMap.put(roomId, amountTimeMap);

        }
        return nosContractGUIDMap;
    }
    

    /**
     * 获取实收nos
     */
    private Map<String, Map<LocalDateTime, BigDecimal>> getNosReceiptsMoney(List<VsNosGetin> nosPayabledataDtoList, List<NosTradeDto> nosContractList) {

        //实收receivableMap（<key:合同id,<key:收款时间,value:金额>>）
        HashMap<String, Map<String, BigDecimal>> receiptsMoneyByRoomIdMap = new HashMap<>();
        for (NosTradeDto nosTradedataDto : nosContractList) {
            String roomId = nosTradedataDto.getRoomId();
            for (VsNosGetin nosGetin : nosPayabledataDtoList) {
                //房间id
                if (roomId.equals(nosGetin.getRoomId())) {
                    //人民币金额(实收明细金额)
                    BigDecimal receiptAmount = nosGetin.getPayableMoney();
                    //实收时间
                    LocalDateTime receiptDate = nosGetin.getPayableDate();
                    //实收年月
                    String yearMonth = LocalDateTimeUtils.formatTime(receiptDate, DateUtils.DATE_JFP_STR);
                    //当前月
                    String nowYearMonth = LocalDateTimeUtils.formatTime(LocalDateTime.now(), DateUtils.DATE_JFP_STR);
                    /**
                     * 实收在当前月不计算数据
                     */
                    if (nowYearMonth.equals(yearMonth)) {
                        continue;
                    }
                    Map<String, BigDecimal> receiptsMoneyByTimeMap = receiptsMoneyByRoomIdMap.get(roomId);

                    if (MapUtils.isEmpty(receiptsMoneyByTimeMap)) {
                        Map<String, BigDecimal> receivableMoneyByTimeMap = new HashMap<>();
                        receivableMoneyByTimeMap.put(yearMonth, receiptAmount);
                        receiptsMoneyByRoomIdMap.put(roomId, receivableMoneyByTimeMap);

                    } else {
                        if (receiptsMoneyByTimeMap.containsKey(yearMonth)) {
                            BigDecimal money = receiptsMoneyByTimeMap.get(yearMonth);
                            receiptsMoneyByTimeMap.put(yearMonth, receiptAmount.add(money));
                        } else {
                            receiptsMoneyByTimeMap.put(yearMonth, receiptAmount);
                        }
                        receiptsMoneyByRoomIdMap.put(roomId, receiptsMoneyByTimeMap);
                    }
                }

            }
        }


        Map<String, Map<LocalDateTime, BigDecimal>> moneyByRoomIdMap = new HashMap<>();

        for (Entry<String, Map<String, BigDecimal>> contractGUIDEntry : receiptsMoneyByRoomIdMap.entrySet()) {
            Map<String, BigDecimal> receivableMoneyByTimeMap = contractGUIDEntry.getValue();

            Map<LocalDateTime, BigDecimal> moneyByTimeMap = new HashMap<>();
            for (Entry<String, BigDecimal> moneyEntry : receivableMoneyByTimeMap.entrySet()) {
                String yearMonth = moneyEntry.getKey();
                LocalDateTime dateTime = LocalDateTimeUtils.toLocalDateTime(yearMonth);
                moneyByTimeMap.put(dateTime, moneyEntry.getValue());
            }
            moneyByRoomIdMap.put(contractGUIDEntry.getKey(), moneyByTimeMap);
        }

        return moneyByRoomIdMap;

    }
    
    
    /**
     * 实际版回款系数生效日期map（key:标准业态，value：生效日期）
     *
     * @param signFirstTime 首次生效日期
     * @param suiteRateMap  套数去化率
     * @return
     */
    private Map<String, LocalDateTime> getEffectiveDatesMap(Map<String, LocalDateTime> signFirstTime, Map<String, BigDecimal> suiteRateMap) {

        Map<String, LocalDateTime> effectiveDateMap = new HashMap<>();

        LocalDateTime effectiveDate = null;
        Set<String> codes = signFirstTime.keySet();
        for (String code : codes) {
            LocalDateTime time = signFirstTime.get(code);
            BigDecimal rate = suiteRateMap.get(code);
            if (rate.doubleValue() == 0) {
                effectiveDateMap.put(code, null);
            }
            if (rate.doubleValue() > 0.1) {
                effectiveDate = time.plusMonths(7);
            }

            effectiveDateMap.put(code, effectiveDate);
        }


        return effectiveDateMap;
    }
    
    
    /**
     * 新建实际版回款系数详情表
     *
     * @param coefficientId         项目id
     * @param standardFormatsReturn 业态第N个月的回款系数
     * @param suiteRateMap          套数去化率
     * @param effectiveDates        生效日期
     */
    public void saveRealityDetail(String coefficientId, HashMap<String, Map<Integer, BigDecimal>> standardFormatsReturn, Map<String, BigDecimal> suiteRateMap, Map<String, LocalDateTime> effectiveDates, Map<String, BigDecimal> signingRateMap, Map<String, LocalDateTime> signFirstTimeMap) {
        ReturnCoefficientDetail coefficientDetail = new ReturnCoefficientDetail();
        //standardFormatsReturn(key:业态编码,value(key:月份，value：系数))
        Set<String> standardCodes = standardFormatsReturn.keySet();
        for (String standardCode : standardCodes) {
            Map<Integer, BigDecimal> map = standardFormatsReturn.get(standardCode);
            //回款系数详情表id
            coefficientDetail.setId(UUIDUtils.create());
            //回款系数版本表id
            coefficientDetail.setCoefficientId(coefficientId);
            //创建人员
            coefficientDetail.setCreateBy(Constants.SYS);
            //创建日期
            coefficientDetail.setCreateTime(LocalDateTime.now());
            //是否删除
            coefficientDetail.setIsDelete(IsDeleteEnum.NO.getKey());
            //签约回款率
            coefficientDetail.setSigningRate(signingRateMap.get(standardCode));
            //首次签约日期
            coefficientDetail.setDateInitialSigning(signFirstTimeMap.get(standardCode));
            //套数去化率
            coefficientDetail.setSuiteSellingRate(suiteRateMap.get(standardCode));
            //生效日期
            coefficientDetail.setEffectiveDate(effectiveDates.get(standardCode));
            //业态编码
            coefficientDetail.setProProductCode(standardCode);
            coefficientDetail.setProProductType(StandardFormatsEnum.getValueByKey(standardCode));
            //三大业态编码
            String code = StandardFormatsMapEnum.getValueByKey(standardCode);
            coefficientDetail.setThreeProductCode(code);
            //三大业态type
            coefficientDetail.setThreeProductName(StandardFormatsEnum.getValueByKey(code));
            //每月回款系数
            Set<Integer> integers = map.keySet();
            for (Integer integer : integers) {
                switch (integer) {
                    case 1:
                        coefficientDetail.setOneMonth(map.get(integer));
                        break;
                    case 2:
                        coefficientDetail.setTwoMonth(map.get(integer));
                        break;
                    case 3:
                        coefficientDetail.setThreeMonth(map.get(integer));
                        break;
                    case 4:
                        coefficientDetail.setFourMonth(map.get(integer));
                        break;
                    case 5:
                        coefficientDetail.setFiveMonth(map.get(integer));
                        break;
                    case 6:
                        coefficientDetail.setSixMonth(map.get(integer));
                        break;
                    case 7:
                        coefficientDetail.setSevenMonth(map.get(integer));
                        break;
                    case 8:
                        coefficientDetail.setEightMonth(map.get(integer));
                        break;
                    case 9:
                        coefficientDetail.setNineMonth(map.get(integer));
                        break;
                    case 10:
                        coefficientDetail.setTenMonth(map.get(integer));
                        break;
                    case 11:
                        coefficientDetail.setElevenMonth(map.get(integer));
                        break;
                    case 12:
                        coefficientDetail.setTwelveMonth(map.get(integer));
                        break;
                    case 13:
                        coefficientDetail.setThirteenMonth(map.get(integer));
                        break;
                    case 14:
                        coefficientDetail.setFourteenMonth(map.get(integer));
                        break;
                    case 15:
                        coefficientDetail.setFifteenMonth(map.get(integer));
                        break;
                    case 16:
                        coefficientDetail.setSixteenMonth(map.get(integer));
                        break;
                    case 17:
                        coefficientDetail.setSeventeenMonth(map.get(integer));
                        break;
                    case 18:
                        coefficientDetail.setEighteenMonth(map.get(integer));
                        break;
                    case 19:
                        coefficientDetail.setNineteenMonth(map.get(integer));
                        break;
                    case 20:
                        coefficientDetail.setTwentyMonth(map.get(integer));
                        break;
                    case 21:
                        coefficientDetail.setTwentyOneMonth(map.get(integer));
                        break;
                    case 22:
                        coefficientDetail.setTwentyTwoMonth(map.get(integer));
                        break;
                    case 23:
                        coefficientDetail.setTwentyThreeMonth(map.get(integer));
                        break;
                    default:
                        coefficientDetail.setTwentyFourMonth(map.get(integer));
                        break;
                }
            }
            returnCoefficientDetailMapper.insert(coefficientDetail);
        }
    }
    
    
    /**
     * 获取标准业态下24个月的回款金额(key：标准业态编码，value（key：24月，value：回款金额）)
     *
     * @param nosContractList    实收单
     * @param nosContractGUIDMap 计算所有房间的回款金额
     * @return
     */
    private Map<String, Map<Integer, BigDecimal>> getStandardMapForNos(List<NosTradeDto> nosContractList, Map<String, Map<Integer, BigDecimal>> nosContractGUIDMap) {
        Map<String, Map<Integer, BigDecimal>> map = new HashMap<>();
        //业态编码（key：业态编码，房间id集合）
        Map<String, List<String>> nosCodesOfRoomMap = nosContractList.stream().collect(Collectors.groupingBy(NosTradeDto::getEstateCode, Collectors.mapping(NosTradeDto::getRoomId, Collectors.toList())));
        for (Map.Entry<String, List<String>> entry : nosCodesOfRoomMap.entrySet()) {
            Map<Integer, BigDecimal> moneyByMonthMap = getMoneyByMonthMap();
            List<String> roomList = entry.getValue();
            nosContractGUIDMap.entrySet().stream()
                    .filter(s -> roomList.contains(s.getKey())).map(Map.Entry::getValue)
                    .forEach(s ->
                            s.entrySet().stream()
                                    .filter(e -> e.getKey() <= 24)
                                    .forEach(e -> {
                                        if (e.getKey() > 24) {
                                            moneyByMonthMap.put(24, moneyByMonthMap.get(24).add(e.getValue()));
                                        } else {
                                            moneyByMonthMap.put(e.getKey(), moneyByMonthMap.get(e.getKey()).add(e.getValue()));
                                        }
                                    }));
            map.put(entry.getKey(), moneyByMonthMap);
        }
        return map;
    }
    
    /**
     * 首次签约日期(key:标准业态编码，value：首次签约日期)
     *
     * @param nosContractList 合同
     * @param receiptsMap     实收
     * @return
     */

    private Map<String, LocalDateTime> getSignFirstTimeMapOfNos(List<NosTradeDto> nosContractList, Map<String, Map<LocalDateTime, BigDecimal>> receiptsMap) {
        //业态编码（key：业态编码，房间id集合）
        Map<String, List<String>> nosCodesOfRoomMap = nosContractList.stream().collect(Collectors.groupingBy(NosTradeDto::getEstateCode, Collectors.mapping(NosTradeDto::getRoomId, Collectors.toList())));
        //首次签约日期(key:标准业态编码，value：首次签约日期)
        HashMap<String, LocalDateTime> signFirstTimeMap = new HashMap<>();
        //房间签约时间(key:房间id，value：签约总金额)
        Map<String, LocalDateTime> roomSignTime = new HashMap<>();
        for (NosTradeDto nosTradedataDto : nosContractList) {
            String roomId = nosTradedataDto.getRoomId();
            BigDecimal tradeMoney = nosTradedataDto.getAmountMoney();
            LocalDateTime tradeDate = nosTradedataDto.getTradeDate();
            //实收单
            Map<LocalDateTime, BigDecimal> timeOfMoneyMap = receiptsMap.get(roomId);
            //实收总额
            BigDecimal totalMoney = BigDecimal.ZERO;
            Set<LocalDateTime> localDateTimes = timeOfMoneyMap.keySet();
            for (LocalDateTime localDateTime : localDateTimes) {
                BigDecimal bigDecimal = timeOfMoneyMap.get(localDateTime);
                totalMoney = totalMoney.add(bigDecimal);
            }
            //房源签约时间存入
            if (totalMoney.subtract(tradeMoney).abs().intValue() < 100) {
                roomSignTime.put(roomId, tradeDate);
            } else {
                roomSignTime.put(roomId, LocalDateTime.MAX);
            }
        }

        Set<String> codes = nosCodesOfRoomMap.keySet();
        for (String code : codes) {
            //签约日期
            LocalDateTime signFirstTime = LocalDateTime.now();
            List<String> roomIds = nosCodesOfRoomMap.get(code);
            for (String roomId : roomIds) {
                LocalDateTime localDate = roomSignTime.get(roomId);
                if (signFirstTime.isAfter(localDate)) {
                    signFirstTime = localDate;
                }
            }
            signFirstTimeMap.put(code, signFirstTime);

        }
        return signFirstTimeMap;
    }
    
    /**
     * 标准业态下实收总金额
     *
     * @param standardMap 标准业态下24个月的回款金额(key：标准业态编码，value（key：24月，value：回款金额）)
     * @return
     */
    private HashMap<String, BigDecimal> getNosGrossAmount(Map<String, Map<Integer, BigDecimal>> standardMap, Map<String, List<String>> nosCodesOfRoomMap) {
        //标准业态下的签约总金额(key:标准业态编码，value：签约总金额)
        HashMap<String, BigDecimal> codeGrossAmount = new HashMap<>();
        Set<String> codes = standardMap.keySet();
        for (String code : codes) {
            BigDecimal totalMoney = BigDecimal.ZERO;
            Map<Integer, BigDecimal> map = standardMap.get(code);
            Set<Integer> integers = map.keySet();
            for (Integer integer : integers) {
                totalMoney = totalMoney.add(map.get(integer));
            }
            codeGrossAmount.put(code, totalMoney);
        }
        return codeGrossAmount;
    }
    
    /**
     * 累计签约回款率
     *
     * @param nosContractList   合同
     * @param receiptsMap       实收
     */
    private Map<String, BigDecimal> getSigningRateForNos( List<NosTradeDto> nosContractList, Map<String, Map<LocalDateTime, BigDecimal>> receiptsMap, Map<String, List<String>> codesContractListMap) {
        Map<String, BigDecimal> signingRatMap = new HashMap<>();
        //签约总金额(key:标准业态编码，value：签约总金额)
        HashMap<String, BigDecimal> grossAmount = getNosContractAmount(nosContractList);
        //房间实收总额(key:房间id，value：实收总金额)
        Map<String, BigDecimal> roomGrossAmount = getNosAllGrossAmount(receiptsMap,codesContractListMap);
        Set<String> standardCodes = roomGrossAmount.keySet();
        for (String standardCode : standardCodes) {
            //标准业态下签约总金额
            BigDecimal grossAmountMoney = grossAmount.get(standardCode);
            if (grossAmountMoney.equals(BigDecimal.ZERO)) {
                continue;
            }
            //标准业态下实收总金额
            BigDecimal practicalMoney = roomGrossAmount.get(standardCode);
            if (practicalMoney.equals(BigDecimal.ZERO)) {
                continue;
            }
            BigDecimal rate = practicalMoney.divide(grossAmountMoney, 5, RoundingMode.HALF_UP);
            signingRatMap.put(standardCode, rate);
        }
        return signingRatMap;
    }
    
    
    /**
     * 获取标准业态下签约合同总额
     *
     * @param nosContractList 合同
     * @return
     */
    private HashMap<String, BigDecimal> getNosContractAmount(List<NosTradeDto> nosContractList) {
        //业态编码（key：业态编码，房间id集合）
        Map<String, List<String>> nosCodesOfRoomMap = nosContractList.stream().collect(Collectors.groupingBy(NosTradeDto::getEstateCode, Collectors.mapping(NosTradeDto::getRoomId, Collectors.toList())));
        //标准业态下的签约总金额(key:标准业态编码，value：签约总金额)
        HashMap<String, BigDecimal> codeGrossAmount = new HashMap<>();
        //房间签约总额(key:房间id，value：签约总金额)
        Map<String, BigDecimal> roomGrossAmount = new HashMap<>();
        for (NosTradeDto nosTradedataDto : nosContractList) {
            String roomId = nosTradedataDto.getRoomId();
            BigDecimal tradeMoney = nosTradedataDto.getAmountMoney();
            roomGrossAmount.put(roomId,tradeMoney);
        }

        Set<String> codes = nosCodesOfRoomMap.keySet();
        for (String code : codes) {
            List<String> roomIds = nosCodesOfRoomMap.get(code);
            //标准业态下房间签约总额
            BigDecimal nosGrossAmount = BigDecimal.ZERO;
            for (String roomId : roomIds) {
                BigDecimal bigDecimal = roomGrossAmount.get(roomId);
                nosGrossAmount = nosGrossAmount.add(bigDecimal);
            }
            codeGrossAmount.put(code, nosGrossAmount);
        }
        return codeGrossAmount;
    }
    
    /**
     * 新建nos实际版回款系数详情表
     */
    private void saveRealityDetailOfNos(String coefficientId, HashMap<String, Map<Integer, BigDecimal>> standardFormatsReturn, Map<String, BigDecimal> suiteRateMap, Map<String, LocalDateTime> effectiveDates, Map<String, LocalDateTime> signFirstTimeMap, Map<String, BigDecimal> signingRateMap) {
        ReturnCoefficientDetail coefficientDetail = new ReturnCoefficientDetail();
        //standardFormatsReturn(key:业态编码,value(key:月份，value：系数))
        Set<String> standardCodes = standardFormatsReturn.keySet();
        for (String standardCode : standardCodes) {
            Map<Integer, BigDecimal> map = standardFormatsReturn.get(standardCode);
            //回款系数详情表id
            coefficientDetail.setId(UUIDUtils.create());
            //回款系数版本表id
            coefficientDetail.setCoefficientId(coefficientId);
            //创建人员
            coefficientDetail.setCreateBy(Constants.SYS);
            //创建日期
            coefficientDetail.setCreateTime(LocalDateTime.now());
            //是否删除
            coefficientDetail.setIsDelete(IsDeleteEnum.NO.getKey());
            //签约回款率
            coefficientDetail.setSigningRate(signingRateMap.get(standardCode));
            //首次签约日期
            coefficientDetail.setDateInitialSigning(signFirstTimeMap.get(standardCode));
            //套数去化率
            coefficientDetail.setSuiteSellingRate(suiteRateMap.get(standardCode));
            //生效日期
            coefficientDetail.setEffectiveDate(effectiveDates.get(standardCode));
            //业态编码
            coefficientDetail.setProProductCode(standardCode);
            coefficientDetail.setProProductType(StandardFormatsEnum.getValueByKey(standardCode));
            //三大业态编码
            String code = StandardFormatsMapEnum.getValueByKey(standardCode);
            coefficientDetail.setThreeProductCode(code);
            //三大业态type
            coefficientDetail.setThreeProductName(StandardFormatsEnum.getValueByKey(code));
            //每月回款系数
            Set<Integer> integers = map.keySet();
            for (Integer integer : integers) {
                switch (integer) {
                    case 1:
                        coefficientDetail.setOneMonth(map.get(integer));
                        break;
                    case 2:
                        coefficientDetail.setTwoMonth(map.get(integer));
                        break;
                    case 3:
                        coefficientDetail.setThreeMonth(map.get(integer));
                        break;
                    case 4:
                        coefficientDetail.setFourMonth(map.get(integer));
                        break;
                    case 5:
                        coefficientDetail.setFiveMonth(map.get(integer));
                        break;
                    case 6:
                        coefficientDetail.setSixMonth(map.get(integer));
                        break;
                    case 7:
                        coefficientDetail.setSevenMonth(map.get(integer));
                        break;
                    case 8:
                        coefficientDetail.setEightMonth(map.get(integer));
                        break;
                    case 9:
                        coefficientDetail.setNineMonth(map.get(integer));
                        break;
                    case 10:
                        coefficientDetail.setTenMonth(map.get(integer));
                        break;
                    case 11:
                        coefficientDetail.setElevenMonth(map.get(integer));
                        break;
                    case 12:
                        coefficientDetail.setTwelveMonth(map.get(integer));
                        break;
                    case 13:
                        coefficientDetail.setThirteenMonth(map.get(integer));
                        break;
                    case 14:
                        coefficientDetail.setFourteenMonth(map.get(integer));
                        break;
                    case 15:
                        coefficientDetail.setFifteenMonth(map.get(integer));
                        break;
                    case 16:
                        coefficientDetail.setSixteenMonth(map.get(integer));
                        break;
                    case 17:
                        coefficientDetail.setSeventeenMonth(map.get(integer));
                        break;
                    case 18:
                        coefficientDetail.setEighteenMonth(map.get(integer));
                        break;
                    case 19:
                        coefficientDetail.setNineteenMonth(map.get(integer));
                        break;
                    case 20:
                        coefficientDetail.setTwentyMonth(map.get(integer));
                        break;
                    case 21:
                        coefficientDetail.setTwentyOneMonth(map.get(integer));
                        break;
                    case 22:
                        coefficientDetail.setTwentyTwoMonth(map.get(integer));
                        break;
                    case 23:
                        coefficientDetail.setTwentyThreeMonth(map.get(integer));
                        break;
                    default:
                        coefficientDetail.setTwentyFourMonth(map.get(integer));
                        break;
                }
            }
            returnCoefficientDetailMapper.insert(coefficientDetail);
        }
    }
    
    /**
     * 根据实收日期获取应收总额
     *
     * @param receiptsTime   实收日期
     * @param receivablesMap 应收
     * @return
     */

    private BigDecimal getReceiptTotalMoney(LocalDateTime receiptsTime, HashMap<LocalDateTime, BigDecimal> receivablesMap) {
        BigDecimal receiptTotalMoney = BigDecimal.ZERO;
        Set<LocalDateTime> receivablesTimes = receivablesMap.keySet();
        for (LocalDateTime receivablesDateTime : receivablesTimes) {
            //应收时间早于或等于实收时间时计算总额
            if (receivablesDateTime.compareTo(receiptsTime) <= 0) {
                BigDecimal bigDecimal = receivablesMap.get(receivablesDateTime);
                receiptTotalMoney = receiptTotalMoney.add(bigDecimal);
            }

        }
        return receiptTotalMoney;
    }
    
    /**
     * 获取标准业态下全部楼栋id列表map
     *
     * @param proProductCodeMap 供货详情 map(key:标准业态编码，value：供货详情列表)
     * @return standardBuild 标准业态下全部楼栋id列表map(key:标准业态编码,value:楼栋id列表)
     */
    private HashMap<String, List<String>> getStandardBuild(Map<String, List<SignPlanVersionSupplyDetail>> proProductCodeMap) {
        //标准业态下全部楼栋id列表map(key:标准业态编码,value:楼栋id列表)
        HashMap<String, List<String>> standardBuild = new HashMap<>();
        for (Map.Entry<String, List<SignPlanVersionSupplyDetail>> entity : proProductCodeMap.entrySet()) {
            //标准业态编码
            String proProductCode = entity.getKey();
            //获取该标准业态下的供货详情列表
            List<SignPlanVersionSupplyDetail> supplyDetails = entity.getValue();
            //获取标准业态下的楼栋id列表
            List<String> buildIds = supplyDetails.stream().map(SignPlanVersionSupplyDetail::getOrgId).collect(Collectors.toList());
            //标准业态下全部楼栋id列表map(key:标准业态编码,value:楼栋id列表)
            standardBuild.put(proProductCode, buildIds);
        }
        return standardBuild;
    }
    
    /**
     * 获取24个月map
     */
    public Map<Integer, BigDecimal> getMoneyByMonthMap() {
        Map<Integer, BigDecimal> moneyByMonthMap = new HashMap<>();
        for (int i = 1; i <= 24; i++) {
            moneyByMonthMap.put(i, BigDecimal.ZERO);
        }
        return moneyByMonthMap;
    }
    
    /**
     * 计算实际签约回款率
     * @param mymContractDtoList 合同
     * @param codesContractListMap  标准业态下合同列表
     * @return
     */
    private Map<String, BigDecimal> getMyGrossAmount(List<MymContractDto> mymContractDtoList, Map<String, List<String>> codesContractListMap) {

        Map<String, BigDecimal> grossAmount=new HashMap<>();

        Set<Entry<String, List<String>>> codeEntries = codesContractListMap.entrySet();
        for (Entry<String, List<String>> codeEntry : codeEntries) {
            List<String> idList = codeEntry.getValue();
            BigDecimal total =BigDecimal.ZERO;
            for (String id : idList) {
                for (MymContractDto mymContractDto : mymContractDtoList) {
                    if (id.equals(mymContractDto.getContractGUID())){
                        BigDecimal cjRmbTotal = mymContractDto.getCjRmbTotal();
                        total=total.add(cjRmbTotal);
                    }
                }
            }
            grossAmount.put(codeEntry.getKey(),total);
        }
        return grossAmount;

    }
    
    /**
     * 计算业态实收总额
     * @param receiptsMap 实收单 （<key:房间id,<key:收款时间,value:金额>>）
     * @param codesContractListMap 标准业态下房间id（key：业态编码，房间id集合）
     * @return 房间实收总额(key:标准业态code，value：实收总金额) Map<String, BigDecimal> roomGrossAmount
     */
    private Map<String, BigDecimal> getNosAllGrossAmount(Map<String, Map<LocalDateTime, BigDecimal>> receiptsMap, Map<String, List<String>> codesContractListMap) {
        //标准业态下的签约总金额(key:标准业态编码，value：实收总金额)
        Map<String, BigDecimal> roomGrossAmount=new HashMap<>();
        //房间实收总额
        Map<String, BigDecimal> codeGrossAmount = new HashMap<>();
        //获取房间实收总额（key:房间id，value：房间实收总额）
        Set<Entry<String, Map<LocalDateTime, BigDecimal>>> entries = receiptsMap.entrySet();
        for (Entry<String, Map<LocalDateTime, BigDecimal>> entry : entries) {
            Map<LocalDateTime, BigDecimal> grossAmount = entry.getValue();
            Set<Entry<LocalDateTime, BigDecimal>> grossAmountMaps = grossAmount.entrySet();
            BigDecimal roomGrossMoney = new BigDecimal(0);
            for (Entry<LocalDateTime, BigDecimal> grossAmountMap : grossAmountMaps) {
                //计算每个房间的实际回款总额
                roomGrossMoney=roomGrossMoney.add(grossAmountMap.getValue());
            }
            codeGrossAmount.put(entry.getKey(),roomGrossMoney);
        }
        //获取标准业态下实收总额
        Set<Entry<String, List<String>>> codes = codesContractListMap.entrySet();
        for (Entry<String, List<String>> code : codes) {
            List<String> roomIdList = code.getValue();
            BigDecimal codeGrossMoney = new BigDecimal(0);
            for (String roomId : roomIdList) {
                BigDecimal roomGrossMoney = codeGrossAmount.get(roomId);
                codeGrossMoney=codeGrossMoney.add(roomGrossMoney);
            }
            roomGrossAmount.put(code.getKey(),codeGrossMoney);
        }
        return roomGrossAmount;
    }
    
    /**
     * 获取标准业态下全部子类code集合
     */
    private Map<String, List<String>> getStandardCodeMap(List<String> singIdList) {
    	List<Map<String, Object>> list = returnCoefficientDetailWrapper.queryProductCodeBySingIdList(singIdList);
    	//map(key:标准业态编码,value：子类编码集合)
        Map<String, List<String>> resMap = new HashMap<>();
    	for(Map<String, Object> map : list) {
    		resMap.put(String.valueOf(map.get("parentCode")), Arrays.asList(String.valueOf(map.get("childCode")).split(",")));
    	}
        return resMap;
    }
    
}
